package com.weilele.mvvm.base.helper

import android.widget.Toast
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import com.weilele.mvvm.MvvmConf
import com.weilele.mvvm.utils.activity.isMainThread
import com.weilele.mvvm.utils.activity.toast
import java.lang.IllegalArgumentException

//对liveData取一个别名，方便操作
typealias StatusLiveData<T> = LiveData<StatusValue<T>>
typealias StatusListLiveData<T> = LiveData<StatusValue<MutableList<T>>>

typealias StatusMutableLiveData<T> = MutableLiveData<StatusValue<T>>
typealias StatusListMutableLiveData<T> = MutableLiveData<StatusValue<MutableList<T>>>

typealias StatusMediatorLiveData<T> = MediatorLiveData<StatusValue<T>>
typealias StatusListMediatorLiveData<T> = MediatorLiveData<StatusValue<MutableList<T>>>

sealed class StatusValue<T>(
        var value: T? = null,
        var msg: String? = null,
        var code: Int? = null
) {
    class Running<T> : StatusValue<T>()

    class Error<T>(nonNullMsg: String) : StatusValue<T>(msg = nonNullMsg) {
        var nonNullMsg: String = nonNullMsg
            get() = this.msg ?: field
            set(value) {
                field = value
                this.msg = value
            }
    }

    class Success<T>(nonNullValue: T) : StatusValue<T>(value = nonNullValue) {
        var nonNullValue: T = nonNullValue
            get() = this.value ?: field
            set(value) {
                field = value
                this.value = value
            }
    }
}


/**
 * 创建一个对象
 */
fun <T> createStatusLiveData() = StatusMutableLiveData<T>()
fun <T> createStatusListLiveData() = StatusListMutableLiveData<T>()

/**
 * 创建一个中介者对象
 */
fun <T> createStatusMediatorLiveData() = StatusMediatorLiveData<T>()

fun <T> createStatusListMediatorLiveData() =
        StatusListMediatorLiveData<T>()

/**
 * 数据是否处于running状态
 */
val <T> StatusValue<T>.isRunning: Boolean
    get() = this is StatusValue.Running

val <T> LiveData<StatusValue<T>>.isRunning: Boolean
    get() = this.value?.isRunning == true

val <T> LiveData<StatusValue<T>>.isNull: Boolean
    get() = this.value == null

/**
 * 数据是否处于成功状态
 */
val <T> StatusValue<T>.isSuccess: Boolean
    get() = this is StatusValue.Success

val <T> LiveData<StatusValue<T>>.isSuccess: Boolean
    get() = this.value?.isSuccess == true

/**
 * 数据是否处于错误状态
 */
val <T> StatusValue<T>.isError: Boolean
    get() = this is StatusValue.Error

val <T> LiveData<StatusValue<T>>.isError: Boolean
    get() = this.value?.isError == true

/**
 * 运行状态
 */
fun <T> LiveData<StatusValue<T>>.running(
        isKeepLastValue: Boolean = true,
        value: T? = null,
        msg: String? = null,
        code: Int? = null
): StatusValue.Running<T> {
    val oldValue = this.value
    val newValue = StatusValue.Running<T>()
    newValue.value = if (isKeepLastValue) value ?: oldValue?.value else value
    newValue.msg = if (isKeepLastValue) msg ?: oldValue?.msg else msg
    newValue.code = if (isKeepLastValue) code ?: oldValue?.code else code
    setStatusValue(newValue)
    return newValue
}


/**
 * 错误状态
 */
fun <T> LiveData<StatusValue<T>>.error(
        msg: String,
        isKeepLastValue: Boolean = true,
        value: T? = null,
        code: Int? = null
): StatusValue.Error<T> {
    val oldValue = this.value
    val newValue = StatusValue.Error<T>(msg)
    newValue.value = if (isKeepLastValue) value ?: oldValue?.value else value
    newValue.code = if (isKeepLastValue) code ?: oldValue?.code else code
    setStatusValue(newValue)
    return newValue
}

/**
 * 成功状态
 */
fun <T> LiveData<StatusValue<T>>.success(
        value: T,
        isKeepLastValue: Boolean = true,
        msg: String? = null,
        code: Int? = null
): StatusValue.Success<T> {
    val oldValue = this.value
    val newValue = StatusValue.Success(value)
    newValue.msg = if (isKeepLastValue) msg ?: oldValue?.msg else msg
    newValue.code = if (isKeepLastValue) code ?: oldValue?.code else code
    setStatusValue(newValue)
    return newValue
}

/**
 * 设置值
 */
private fun <T> LiveData<StatusValue<T>>.setStatusValue(statusValue: StatusValue<T>) {
    if (isMainThread()) {
        when (this) {
            is MutableLiveData -> {
                this.value = statusValue
            }
            is MediatorLiveData -> {
                this.value = statusValue
            }
        }
    } else {
        when (this) {
            is MutableLiveData -> {
                this.postValue(statusValue)
            }
            is MediatorLiveData -> {
                this.postValue(statusValue)
            }
        }
    }
}

/**
 * 带有运行状态的请求
 * 中间代码块运行时间处于running状态，成功返回结果，需要自己转换为success状态
 * 常用于 协程状态下 对网络发起的请求
 * suspend fun loadBannerPicByRetrofit(date: MutableLiveData<StatusValue<BannerPicBean>>): BannerPicBean? {
 *        return date.withStatus {
 *                    Retrofit.picApi.bannerPic()
 *                  }
 *          }
 */
inline fun <T, P> LiveData<StatusValue<P>>.withStatus(
        noinline errorCall: ((Throwable) -> T?)? = null,
        noinline resultCall: ((T?) -> Unit)? = null,
        call: LiveData<StatusValue<P>>.() -> T?
): T? {
    this.running()
    return try {
        val result = call.invoke(this)
        resultCall?.invoke(result)
        result
    } catch (e: Throwable) {
        e.printStackTrace()
        this.error(e.message ?: "$e")
        errorCall?.invoke(e)
    }
}

/**
 * 统一捕获异常
 * 可用一个类统一处理
 */
inline fun <T> withError(
        noinline errorCall: ((Throwable) -> T?)? = null,
        noinline resultCall: ((T?) -> Unit)? = null,
        call: () -> T?
): T? {
    return try {
        val result = call.invoke()
        resultCall?.invoke(result)
        result
    } catch (e: Throwable) {
        e.printStackTrace()
        errorCall?.invoke(e)
    }
}

/**
 * 忽略错误,默认提示捕获的异常
 */
inline fun <T> ignoreErrorToast(isToastError: Boolean = true, call: () -> T?): T? {
    return ignoreError(isToastError, call)
}

/**
 * 忽略错误,默认不提示异常
 */
inline fun <T> ignoreError(isToastError: Boolean = false, call: () -> T?): T? {
    return try {
        call.invoke()
    } catch (e: Throwable) {
        e.printStackTrace()
        if (isToastError) {
            e.message?.toast()
        }
        null
    }
}

fun logStackTrace(msg: String?) {
    if (MvvmConf.IS_DEBUG) {
        Throwable("调用堆栈打印------------->$msg").printStackTrace()
    }
}

/**
 * 扩展属性
 */
val <T> LiveData<StatusValue<T>>.data: T?
    get() = this.value?.value


/**
 * 在挂起函数运行一段代码，并将错误和结果绑定在liveData，
 * 运行状态实时与liveData绑定
 * Obj:代码运行的对象作用域
 */
interface LiveDataRunnable {
    suspend operator fun <Bean> invoke(
            data: StatusLiveData<Bean>?,
            isToastError: Boolean = true/*是否toast出错误*/,
            isKeepLastValue: Boolean = true/*是否保留之前已经赋值的数据*/,
            errorCall: ((Throwable) -> Bean?)? = null,
            run: suspend () -> Bean?
    )
}

/**
 * [LiveDataRunnable]默认的请求处理
 */
class DefaultLiveDataRunnable : LiveDataRunnable {
    override suspend fun <Bean> invoke(
            data: StatusLiveData<Bean>?,
            isToastError: Boolean,
            isKeepLastValue: Boolean,
            errorCall: ((Throwable) -> Bean?)?,
            run: suspend () -> Bean?
    ) {
        try {
            data?.running(isKeepLastValue)
            val result = run.invoke()
            if (result == null) {
                val errorMsg = "call result is null"
                val errorResult = errorCall?.invoke(Throwable(errorMsg))
                if (errorResult == null) {
                    //这里不弹出提示
                    data?.error(errorMsg, isKeepLastValue)
                } else {
                    data?.success(errorResult, isKeepLastValue)
                }
            } else {
                data?.success(result, isKeepLastValue)
            }
        } catch (e: Throwable) {
            e.printStackTrace()
            val errorResult = errorCall?.invoke(e)
            if (errorResult == null) {
                val errorMsg = e.message ?: "$e"
                if (isToastError) {
                    errorMsg.toast(Toast.LENGTH_LONG)
                }
                data?.error(errorMsg, isKeepLastValue)
            } else {
                data?.success(errorResult, isKeepLastValue)
            }
        }
    }
}